package pers.xx.sbm.realm;

import com.alibaba.fastjson.JSONObject;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import pers.xx.sbm.entity.User;
import pers.xx.sbm.service.MenuService;
import pers.xx.sbm.service.UserRoleService;
import pers.xx.sbm.service.UserService;
import pers.xx.sbm.utils.IpUtils;
import pers.xx.sbm.utils.TokenUtils;
import pers.xx.sbm.utils.UserUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author ：xiexing
 * @description：授权认证
 * @date ：2020/9/20 17:41
 */
public class UserRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;

    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private UserUtils userUtils;

    @Autowired
    private MenuService menuService;

    //用户登录次数计数  redisKey 前缀
    private String SHIRO_LOGIN_COUNT = "shiro_login_count_";

    //用户登录是否被锁定  redisKey 前缀
    private String SHIRO_IS_LOCK = "shiro_is_lock_";


    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        User user = userUtils.getCurrentUser();//获取当前用户
        Set<String> perms = menuService.listUserPerms(user.getId());
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(perms);
        return info;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws
            AuthenticationException {
        if (authenticationToken.getCredentials() == null) {
            return null;
        }
        //获得当前用户的用户名
        String userName = (String) authenticationToken.getPrincipal();
        String password = new String((char[]) authenticationToken.getCredentials());
        //从数据库中根据用户名查找用户(这儿的userName实际上是手机号)
        User user = userService.findByUserPhone(userName);
        if (user == null) {
            throw new DisabledAccountException("用户名不存在！");
        }
        if (!password.equals(user.getUserPassword())) {
            throw new DisabledAccountException("用户名或密码错误");
        }
        if (user.getUserIsEnabled() == 0) {
            throw new DisabledAccountException("用户账号已被禁用，请联系系统管理员。");
        }
        //访问一次，计数一次
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
        int loginCount = 0;//登录次数
        String t = opsForValue.get(SHIRO_LOGIN_COUNT + userName);
        if (t != null) {
            loginCount = Integer.valueOf(t);
        }
        opsForValue.increment(SHIRO_LOGIN_COUNT + userName, ++loginCount);  //每次增加1

        //如果这个账号登陆异常，则在登陆页面提醒。
        if (Integer.parseInt(opsForValue.get(SHIRO_LOGIN_COUNT + userName)) >= 3) {
            if ("LOCK".equals(opsForValue.get(SHIRO_IS_LOCK + userName))) {
                //计数大于3次，设置用户被锁定一分钟
                throw new DisabledAccountException("由于输入错误次数大于3次，帐号1分钟内已经禁止登录！");
            }
        }
        //实现锁定
        if (Integer.parseInt(opsForValue.get(SHIRO_LOGIN_COUNT + userName)) >= 3) {
            opsForValue.set(SHIRO_IS_LOCK + userName, "LOCK");  //锁住这个账号，值是LOCK。
            stringRedisTemplate.expire(SHIRO_IS_LOCK + userName, 1, TimeUnit.MINUTES);  //expire  变量存活期限
        }

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletResponse response = attributes.getResponse();
        String token = TokenUtils.creatToken(IpUtils.getIpAddr(), userName);
        Cookie cookie = new Cookie("userCookie", token);
        response.addCookie(cookie);
        opsForValue.set("user:" + token, JSONObject.toJSONString(user));
        stringRedisTemplate.expire("user:" + token, 30, TimeUnit.MINUTES);  //设置存活时间
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUserName(),
                user.getUserPassword(), getName());
        //清空登录计数
        opsForValue.set(SHIRO_LOGIN_COUNT + userName, "0");
        //清空锁
        opsForValue.set(SHIRO_IS_LOCK + userName, "");
        return info;
    }


}
